﻿using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.Common;

namespace GDM.Hogar.Repositorios.Soporte
{
    public class DbManager : IDbManager
    {
        private const string _baseDatosPorDefecto = "Hogar";
        private Database _baseDatos;
        private Stack _transacciones;

        public void GuardarCambios()
        {
            var transacciones = ObtenerTransacciones();
            try
            {
                var transaccion = (IDbTransaction)transacciones.Peek();
                transaccion.Commit();
                transacciones.Pop();
            }
            catch (Exception)
            {
                //TODO: loguear la excepcion
            }
        }

        public void EjecutarConsulta(CommandType tipoComando, string consultaSql)
        {
            var transacciones = ObtenerTransacciones();
            if (transacciones.Count > 0)
            {
                ObtenerBaseDatos().ExecuteNonQuery((DbTransaction)transacciones.Peek(), tipoComando, consultaSql);
            }
            else
            {
                ObtenerBaseDatos().ExecuteNonQuery(tipoComando, consultaSql);
            }
        }

        public void EjecutarConsulta(DbCommand comando)
        {
            var transacciones = ObtenerTransacciones();
            if (transacciones.Count > 0)
            {
                ObtenerBaseDatos().ExecuteNonQuery(comando, (DbTransaction)transacciones.Peek());
            }
            else
            {
                ObtenerBaseDatos().ExecuteNonQuery(comando);
            }
        }

        public IDataReader EjecutarLector(CommandType tipoComando, string consultaSql)
        {
            var transacciones = ObtenerTransacciones();
            return transacciones.Count > 0 ? ObtenerBaseDatos().ExecuteReader((DbTransaction)transacciones.Peek(), tipoComando, consultaSql) : ObtenerBaseDatos().ExecuteReader(tipoComando, consultaSql);
        }

        public IDataReader EjecutarLector(DbCommand comando)
        {
            var transacciones = ObtenerTransacciones();
            return transacciones.Count > 0 ? ObtenerBaseDatos().ExecuteReader(comando, (DbTransaction)transacciones.Peek()) : ObtenerBaseDatos().ExecuteReader(comando);
        }

        public T EjecutarEscalar<T>(DbCommand comando)
        {
            var transacciones = ObtenerTransacciones();
            var resultado = transacciones.Count > 0 ? ObtenerBaseDatos().ExecuteScalar(comando, (DbTransaction)transacciones.Peek()) : ObtenerBaseDatos().ExecuteScalar(comando);

            return (T)Convert.ChangeType(resultado, typeof(T));
        }

        public object ObtenerValorParametro(DbCommand comando, string nombreParametro)
        {
            return ObtenerBaseDatos().GetParameterValue(comando, nombreParametro);
        }

        public DbCommand ObtenerComandoProcedimientoAlmacenado(string nombreProcedimientoAlmacenado)
        {
            return ObtenerBaseDatos().GetStoredProcCommand(nombreProcedimientoAlmacenado);
        }

        public void AbrirTransaccion()
        {
            var transacciones = ObtenerTransacciones();

            var conexion = transacciones.Count == 0 ? ObtenerBaseDatos().CreateConnection() : TransactionScopeConnections.GetConnection(ObtenerBaseDatos()).Connection;

            if (conexion.State != ConnectionState.Open)
            {
                conexion.Open();
            }

            transacciones.Push(conexion.BeginTransaction());
        }

        public void RevertirCambios()
        {
            var transacciones = ObtenerTransacciones();
            try
            {
                var transaccion = (IDbTransaction)transacciones.Pop();
                transaccion.Rollback();
            }
            catch (Exception)
            {
                //TODO: loguear la excepcion
            }
        }

        protected Database ObtenerBaseDatos()
        {
            if (_baseDatos != null) return _baseDatos;

            if (_baseDatos == null)
            {
                //obtiene la cadena de conexion desde el archivo de configuracion de la aplicacion
                string cadenaConexionBd = ConfigurationManager.ConnectionStrings[_baseDatosPorDefecto].ConnectionString;

                //set the database
                _baseDatos = new SqlDatabase(cadenaConexionBd);
            }

            return _baseDatos;
        }

        protected Stack ObtenerTransacciones()
        {
            return _transacciones ?? (_transacciones = new Stack());
        }
    }
}